package com.aggregate.pay.swiftpass.support;

import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import com.aggregate.pay.swiftpass.bean.AbstractData;
import com.aggregate.pay.swiftpass.bean.AbstractResult;
import com.aggregate.pay.swiftpass.support.utils.XML;
import com.fasterxml.jackson.core.type.TypeReference;

/**
 * 接口API操作类抽象类
 * @author Moyq5
 * @date 2017年9月29日
 * @param <D>
 * @param <R>
 */
public abstract class AbstractClient<D extends AbstractData, R extends AbstractResult> implements Client<D, R> {

	private Merch merch;
	
	public AbstractClient(Merch merch) {
		this.merch = merch;
	}
	
	@Override
	public R execute(D data) {
		R resResult = null;
		try {
			data.setMchId(merch.mchId());
			data.setNonceStr(mkRandomStr(32));
			
			String json = XML.toString(data);
			FieldMap map = XML.toObject(json, new TypeReference<FieldMap>(){});
			String sign = null;
			if (isRsa256(data.getSignType())) {
				sign = Tool.rsaSign(map, merch.priKey());
			} else if (isMd5(data.getSignType())) {
				sign = Tool.md5Sign(map, merch.md5Key());
			}
			map.put("sign", sign);
			String reqBody = XML.toString(map);
			HttpClient client = Factory.getHttpClient();
			String resBody = client.post(getServerPath(), reqBody);
			resResult = XML.toObject(resBody, getResultClass());
			if (resResult.getStatus() == 0) {
				map = XML.toObject(resBody, new TypeReference<FieldMap>(){});
				if (isRsa256(data.getSignType())) {
					if (!Tool.rsaVerify(map, Factory.getConfig().pubKey())) {
						throw new Exception("响应内容签名验证失败");
					}
				} else if(isMd5(data.getSignType())) {
					sign = map.get("sign");
					if (!Tool.md5Sign(map, merch.md5Key()).equals(sign)){
						throw new Exception("响应内容签名验证失败");
					}
				}
			}
			
		} catch (UnknownHostException | SocketTimeoutException | SocketException e) {
			log.error("网络异常", e);
			resResult = newResultInstance();
			resResult.setStatus(-1);
			resResult.setMessage("网络异常：" + e.getMessage());
		} catch (Exception e) {
			log.error("接口异常", e);
			if (null == resResult) {
				resResult = newResultInstance();
			}
			resResult.setStatus(-1);
			resResult.setMessage("接口异常：" + e.getMessage());
		}
		return resResult;
	}
	
	protected abstract Class<R> getResultClass();
	
	protected String getServerPath() {
		return Factory.getConfig().serverUrl();
	}
	
	private R newResultInstance() {
		try {
			return getResultClass().newInstance();
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		return null;
	}

	private static String mkRandomStr(int length) {
		final char[] chars = "0123456789ABCDEF".toCharArray();
		StringBuffer sb = new StringBuffer();
	    for(int i = 0; i<length; i++){
	    	sb.append(chars[new Long(Math.round(Math.random() * (chars.length-1))).intValue()]);
	    }
		return sb.toString();
	}
	
	private static boolean isRsa256(String signType) {
		return null != signType && signType.equals("RSA_1_256");
	}
	
	private static boolean isMd5(String signType) {
		return null == signType || signType.isEmpty() || signType.equals("MD5");
	}
}
